<?php if ( ! defined('BASEPATH')) exit('This file does not run directly. Sorry.'); ?>
<?php
/**
 * Kalaisoo.
 *
 * A lightweight no-framework framework written in PHP.
 *
 * @package Kalaisoo
 * @author $Author: info@sah-company.com $
 * @version $Id: pagination.php 126 2012-01-11 20:59:15Z info@sah-company.com $
 */

/**
 * Kalaisoo Pagination Class.
 *
 * @package Kalaisoo
 * @subpackage System
 * @author $Author: info@sah-company.com $
 */
class Pagination
{
	/**
	 * the newline character.
	 *
	 * @const string
	 */
	const NEWLINE = "\n";

	/**
	 * the base url.
	 *
	 * @var string
	 */
	protected $base_url;
	
	/**
	 * the order field.
	 *
	 * @var string
	 */
	protected $order;

	/**
	 * the order direction.
	 *
	 * @var string
	 */
	protected $dir;

	/**
	 * the current page number.
	 *
	 * @var int
	 */
	protected $page;
	
	/**
	 * the maximum number of pages.
	 *
	 * @var int
	 */
	protected $max_pages;
	
	/**
	 * how man beans (items, records) per page.
	 *
	 * @var int
	 */
	protected $rows_per_page;
	
	/**
	 * how man beans (items, records) are there in total.
	 *
	 * @var int
	 */
	protected $total_rows;
	
	/**
	 * the number of page links to adjacent.
	 *
	 * @var int
	 */
	protected $adjacents = 2;
	
	/**
	 * wether there is a previous page or not.
	 *
	 * @var bool
	 */
	protected $has_previous_page = false;
	
	/**
	 * wether there is a next page or not.
	 *
	 * @var bool
	 */
	protected $has_next_page = false;
	
	/**
	 * Constructs a new Pagination.
	 *
	 * @param string $url
	 * @param int $page
	 * @param int $rows_per_page
	 * @param int $total_rows
	 * @param string $order
	 * @param string $dir
	 */
	public function __construct($url, $page, $rows_per_page, $total_rows, $order, $dir)
	{
		$this->url = $url;
		$this->page = $page;
		$this->rows_per_page = $rows_per_page;
		$this->total_rows = $total_rows;
		$this->order = $order;
		$this->dir = $dir;
	}
	
	/**
	 * returns a string with HTML.
	 *
	 * The parameter decides which portion of the pagination HTML you get returned.
	 * If type is prev only the previous page link is returned, next dito and all
	 * gives you a prev link, a list of links to pages and the next link.
	 *
	 * @param string The output type, e.g. prev only, next only, all
	 * @return string $pagination
	 */
	public function output($type = null)
	{
		$this->calculate();
		switch ($type) {
			case 'prev':
				$s = $this->build_html_pagelink_prev();
				break;
			case 'next':
				$s = $this->build_html_pagelink_next();
				break;
			default:
				$s = $this->build_html_pagination();
		}
		return $s;
	}
	
	/**
	 * calculates pagination values.
	 *
	 * @return void
	 */
	protected function calculate()
	{	
		if ($this->rows_per_page)
		{
			$this->max_pages = ceil($this->total_rows / $this->rows_per_page);
		}
		else
		{
			$this->max_pages = 1;
		}
		$this->page = max(1, $this->page);
		$this->page = min($this->max_pages, $this->page);
		$this->has_previous_page = $this->page > 1;
		$this->has_next_page = $this->page < $this->max_pages;
	}
	
	/**
	 * returns a string with the pagination HTML.
	 *
	 * @return string $html
	 */
	protected function build_html_pagination()
	{
		if ($this->max_pages <= 1)
		{
			// uups, there is only one page in total
			return '';
		}
		
		// open unordered list
		$s = '<ul class="pagination">'.self::NEWLINE;
		
		// prev page
		$s .= '<li>'.$this->build_html_pagelink_prev().'</li>'.self::NEWLINE;
		
		// digg style pagination list
		$s .= $this->build_html_pagelinks();
		
		// next page
		$s .= '<li>'.$this->build_html_pagelink_next().'</li>'.self::NEWLINE;
		
		$s .= '</ul>'.self::NEWLINE;
		return $s;
	}
	
	/**
	 * returns a string whth the HTML for the previous page link.
	 *
	 * @return string
	 */
	protected function build_html_pagelink_prev()
	{
		// prev page
		if ($this->has_previous_page)
		{
			// with link
			$query = array(
				$this->page - 1,
				$this->rows_per_page,
				$this->order,
				$this->dir
			);
			$s = '<a href="'.ahref($this->url.implode('/', $query)).'">'. __('previous_page').'</a>';
		}
		else
		{
			// without link
			$s = __('previous_page');
		}
		return $s;
	}
	
	/**
	 * returns a string whth the HTML for the next page link.
	 *
	 * @return string The HTML for the next page link
	 */
	protected function build_html_pagelink_next()
	{
		// prev page
		if ($this->has_next_page)
		{
			// with link
			$query = array(
				$this->page + 1,
				$this->rows_per_page,
				$this->order,
				$this->dir
			);
			$s = '<a href="'.ahref($this->url.implode('/', $query)).'">'. __('next_page').'</a>';
		}
		else
		{
			// without link
			$s = __('next_page');
		}
		return $s;
	}
	
	/**
	 * returns a string with the HTML for a list with linls to certain pages.
	 *
     * Portions of this code are from this page.
     * @see http://www.strangerstudios.com/sandbox/pagination/diggstyle.php
     *
	 * @todo refactor code so DRY applies in this code
	 * @todo get rid of magic numbers
	 *
	 * @return string $html
	 */
	protected function build_html_pagelinks()
	{
		$s = '';
		if ($this->max_pages < 7 + ($this->adjacents * 2))
		{
			// not so much possible pages
	        for ($n = 1; $n <= $this->max_pages; $n++)
			{
				$s .= '<li>';
	            if ($n != $this->page)
				{
					// other than current page
					$query = array(
						$n,
						$this->rows_per_page,
						$this->order,
						$this->dir
					);
					$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">'.$n.'</a>';
				}
				else
				{	
					// current page
					$s .= '<span class="current">'.$n.'</span>';
				}
				$s .= '</li>'.self::NEWLINE;
			}
		}
		elseif ($this->max_pages >= 7 + ($this->adjacents * 2))
		{
			// hide some pages
			if ($this->page < 1 + ($this->adjacents * 3))
			{
				// At the beginning, hide pages at the end
	            for ($n = 1; $n < 4 + ($this->adjacents * 2); $n++)
				{
					$s .= '<li>';
	                if ($n != $this->page)
					{
						// other than current page
						$query = array(
							$n,
							$this->rows_per_page,
							$this->order,
							$this->dir
						);
						$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">'.$n.'</a>';
					}
					else
					{
						$s .= '<span class="current">'.$n.'</span>';
					}
					$s .= '</li>'.self::NEWLINE;
				}
				// ellipsis
				$s .= '<li>&#8230</li>'.self::NEWLINE;
				$s .= '<li>';
				$query = array(
					$this->max_pages - 1,
					$this->rows_per_page,
					$this->order,
					$this->dir
				);
				$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">'.($this->max_pages - 1).'</a>';
				$s .= '</li>'.self::NEWLINE;
				$s .= '<li>';
				$query = array(
					$this->max_pages,
					$this->rows_per_page,
					$this->order,
					$this->dir
				);
				$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">'.$this->max_pages.'</a>';
				$s .= '</li>'.self::NEWLINE;
			}
			elseif ($this->max_pages - ($this->adjacents * 2) > $this->page && $this->page > ($this->adjacents * 2))
			{
				
				// In the middle, hide pages beginning and end
				$s .= '<li>';
				$query = array(
					1,
					$this->rows_per_page,
					$this->order,
					$this->dir
				);
				$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">1</a>';
				$s .= '</li>'.self::NEWLINE;
				$s .= '<li>';
				$query = array(
					2,
					$this->rows_per_page,
					$this->order,
					$this->dir
				);
				$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">2</a>';
				$s .= '</li>'.self::NEWLINE;
				$s .= '<li>&#8230</li>'.self::NEWLINE;

	            for ($n = $this->page - $this->adjacents; $n <= $this->page + $this->adjacents; $n++)
				{
					$s .= '<li>';
	                if ($n != $this->page)
					{
						// other than current page
						$query = array(
							$n,
							$this->rows_per_page,
							$this->order,
							$this->dir
						);
						$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">'.$n.'</a>';
					}
					else
					{
						$s .= '<span class="current">'.$n.'</span>';
					}
					$s .= '</li>'.self::NEWLINE;
				}
				
				$s .= '<li>&#8230</li>'.self::NEWLINE;
				
				$s .= '<li>';
				$query = array(
					$this->max_pages - 1,
					$this->rows_per_page,
					$this->order,
					$this->dir
				);
				$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">'.($this->max_pages - 1).'</a>';
				$s .= '</li>'.self::NEWLINE;
				$s .= '<li>';
				$query = array(
					$this->max_pages,
					$this->rows_per_page,
					$this->order,
					$this->dir
				);
				$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">'.$this->max_pages.'</a>';
				$s .= '</li>'.self::NEWLINE;
				
			}
			else
			{
				// At the end, hide pages from the beginning
				$s .= '<li>';
				$query = array(
					1,
					$this->rows_per_page,
					$this->order,
					$this->dir
				);		
				$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">1</a>';
				$s .= '</li>'.self::NEWLINE;
				$s .= '<li>';
				$query = array(
					2,
					$this->rows_per_page,
					$this->order,
					$this->dir
				);
				$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">2</a>';		
				$s .= '</li>'.self::NEWLINE;
				$s .= '<li>&#8230</li>'.self::NEWLINE;
			
	            for ($n = $this->max_pages - (1 + ($this->adjacents * 3)); $n <= $this->max_pages; $n++)
				{
					$s .= '<li>';
	                if ($n != $this->page)
					{
						// other than current page
						$query = array(
							$n,
							$this->rows_per_page,
							$this->order,
							$this->dir
						);
						$s .= '<a href="'.ahref($this->url.implode('/', $query)).'">'.$n.'</a>';
					}
					else
					{
						$s .= '<span class="current">'.$n.'</span>';
					}
					$s .= '</li>'.self::NEWLINE;
				}

			}
		}
		return $s;
	}
}
?>